# -*- coding: utf-8 -*-
# @Time : 2023/01/05
# @Author : hpp
# @File : suoyi.py
# @Software: vscode
"""
核心修改类 SuoYi版本
"""
import os
from core import base


class SuoYi(base.BaseCore):
    def start(self):
        self.series = 'SuoYi'
        # 查找项目根目录
        self.rootpath, self.rootname = self.find_root_dir(
            self.targetdir)
        # 1.修改站点名称
        self.messagehandle('正在修改标题和修站点名称...')
        self.__alter_site_name_and_title()
        self.messagehandle('站点名称和标题修改完成!')
        # 修改js中的导出方法引用的mybatis查询sql路径及mybatis-config.xml
        self.messagehandle('正在修改js的导出sql路径及xml的包路径')
        self.__alter_js_export_sql_mapper()
        self.messagehandle('js的导出sql路径及xml的包路径修改完成')
        # 2.修改包名和项目名
        self.messagehandle('正在修改包名和项目名...')
        self.__alter_package_name_and_project_name(self.rootpath)
        self.messagehandle('包名和项目名修改完成!')
        # 3.修改项目配置和日志配置
        self.messagehandle('正在修改项目配置和日志配置...')
        self.__alter_bootstrapyml_and_logbackxml(self.rootpath)
        self.messagehandle('项目配置和日志配置修改完成!')
        # 4.修改Nacos配置
        self.messagehandle('正在修改Nacos配置...')
        self.__alter_nacos_config(os.path.join(self.rootpath, 'sql'))
        self.messagehandle('Nacos配置修改完成!')
        # 5.修改pom.xml文件
        self.messagehandle('正在修改pom.xml...')
        self.__alter_pom_xml(self.rootpath)
        self.messagehandle('pom.xml修改完成!')
        # 6.修改Application启动类中的站点名称
        self.messagehandle('正在修改Application启动类...')
        self.__alter_application_class()
        self.messagehandle('Application启动类修改完成!')
        # 7.修改目录结构
        self.messagehandle('正在修改目录结构...')
        self.__alter_project_dir()
        self.messagehandle('目录结构修改完成!')

        if len(self.exceptions) > 0:
            self.messagehandle('\r发现有异常信息')
            self.messagehandle('-------------------\n\r')
            for e in self.exceptions:
                self.messagehandle(e)
            self.messagehandle('\r----------------------')

    def __alter_site_name_and_title(self):
        """修改站点名称和网站标题"""
        ntuple = tuple(
            eval(self.templatedict[self.series + '.site_resources_path_tuple']))
        default_site_name = self.templatedict[self.series +
                                              '.default_site_name']
        for item in ntuple:
            filepath = os.path.join(self.rootpath,
                                    item.replace('#', os.path.sep))
            if not os.path.exists(filepath):
                continue
            try:
                with open(filepath, 'r',
                          encoding=self.DEFAULT_ENCODING) as srcfile, open(
                              '%s.bak' % filepath,
                              'w',
                              encoding=self.DEFAULT_ENCODING) as desfile:
                    for line in srcfile:
                        if default_site_name in line:
                            line = line.replace(
                                default_site_name, self.sitename)
                        if ('索菲亚' + default_site_name) in line:
                            line = line.replace(('索菲亚' + default_site_name), self.sitename)
                        desfile.write(line)
                # 移除旧文件
                os.remove(filepath)
                # 重命名备份文件为新文件
                os.rename('%s.bak' % filepath, filepath)
            except Exception as err:
                self.exceptionhandle('修改站点名称和网站标题异常\n修改文件：{}\n异常信息：{}'.format(
                    filepath, err))

    def __alter_js_export_sql_mapper(self):
        """修改js中的导出sql路径"""
        default_package_name = self.templatedict[self.series + '.default_package_name']
        js_xml_resources_path_tuple = tuple(
            eval(self.templatedict[self.series + '.js_xml_resources_path_tuple']))
        for item in js_xml_resources_path_tuple:
            filepath = os.path.join(self.rootpath,
                                    item.replace('#', os.path.sep))
            try:
                self.__replace_js_xml_mapper_recursive(filepath, default_package_name)
            except Exception as err:
                self.exceptionhandle('修改站点名称和网站标题异常\n修改文件：{}\n异常信息：{}'.format(
                    filepath, err))

    def __replace_js_xml_mapper_recursive(self, filepath, default_package_name):
        """递归找到.js/.xml中的包名, 进行修改"""
        if not os.path.exists(filepath):
            return
        # 如果是文件夹且有内容则进行递归
        if os.path.isdir(filepath) and os.listdir(filepath):
            for sub_dir in os.listdir(filepath):
                dir_join_path = os.path.join(filepath, sub_dir)
                self.__replace_js_xml_mapper_recursive(dir_join_path, default_package_name)
            return
        default_project_name = self.templatedict[self.series + '.default_project_name']
        # 是否.js结尾 是则将包名进行替换
        if filepath.endswith('.js') or filepath.endswith('.xml'):
            self.__open_file_and_replace_keyword(filepath, default_package_name, self.packagename)
            # 修改文件中的suoyi为指定项目名称
            self.__open_file_and_replace_keyword(filepath, default_project_name, self.projectname)
        # 修改js文件名
        file_name = filepath.split(os.path.sep)[-1]
        if file_name == default_project_name + ".js":
            os.rename(filepath, filepath.replace(file_name, self.projectname + ".js"))

    def __alter_package_name_and_project_name(self, rootpath):
        """修改包名和项目名称"""
        files = os.listdir(rootpath)
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        default_site_name = self.templatedict[self.series +
                                              '.default_site_name']
        for filename in files:
            filepath = os.path.join(rootpath, filename)
            if os.path.isdir(filepath):
                self.__alter_package_name_and_project_name(filepath)
            else:
                if filename.endswith('.java') or filename.endswith(
                        '.yml'
                ) or filename.endswith('Mapper.xml') or filename.endswith(
                        'logback.xml') or filename.endswith(
                            '.factories') or filename.endswith(
                                '.vm') or filename.endswith(
                                    '.bat') or filename.endswith('.sh') or filename == 'dockerfile':
                    try:
                        encoding = self.get_encoding(filename)
                        with open(filepath, 'r',
                                  encoding=encoding) as srcfile, open(
                                      '%s.bak' % filepath,
                                      'w',
                                      encoding=encoding) as desfile:
                            self.messagehandle('正在修改：' + filename)
                            for line in srcfile:
                                if default_package_name in line:
                                    line = line.replace(
                                        default_package_name,
                                        self.packagename)
                                if default_project_name + '-' in line:
                                    line = line.replace(
                                        default_project_name + '-',
                                        self.projectname + '-')
                                if default_site_name + '（' + default_project_name + '）' in line:
                                    line = line.replace(
                                        default_site_name + '（' + default_project_name + '）',
                                        self.sitename + '（' + self.projectname + '）')
                                if self.configdict['config.enable'] == 'True':
                                    if filename.endswith('.yml'):
                                        line = self.__check_yml_or_sql_config(
                                            line, filename)
                                desfile.write(line)
                        # 移除旧文件
                        os.remove(filepath)
                        # 重命名备份文件为新文件
                        os.rename('%s.bak' % filepath, filepath)
                    except Exception as err:
                        self.exceptionhandle(
                            '修改包名和项目名称异常\n修改文件：{}\n异常信息：{}'.format(
                                filepath, err))

    def __alter_bootstrapyml_and_logbackxml(self, rootpath):
        """
        修改项目bootstrap.yml和logback.xml中的模块名

        参数:
            rootpath (str): 根路径
        """
        # 循环修改指定后缀名的文件内容
        files = os.listdir(rootpath)
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        for filename in files:
            filepath = os.path.join(rootpath, filename)
            # 如果是目录继续递归
            if os.path.isdir(filepath):
                self.__alter_bootstrapyml_and_logbackxml(filepath)
            else:
                try:
                    # 如果是文件才进行修改
                    if filename.endswith('.yml') or filename.endswith(
                            'logback.xml'):
                        with open(
                                filepath, 'r', encoding=self.DEFAULT_ENCODING
                        ) as srcfile, open(
                                '%s.bak' % filepath,
                                'w',
                                encoding=self.DEFAULT_ENCODING) as desfile:
                            self.messagehandle('正在修改：' + filename)
                            yml_group_name = 'group: ' + default_project_name.upper()
                            for line in srcfile:
                                if default_project_name in line:
                                    line = line.replace(
                                        default_project_name,
                                        self.projectname)
                                elif yml_group_name in line:
                                    # 修改.yml文件中group: SUOYI 关键字为 [group: 项目名大写]
                                    line = line.replace(yml_group_name, 'group: ' + self.projectname.upper())
                                desfile.write(line)
                        # 移除旧文件
                        os.remove(filepath)
                        # 重命名备份文件为新文件
                        os.rename('%s.bak' % filepath, filepath)
                except Exception as err:
                    self.exceptionhandle(
                        '修改项目bootstrap.yml和logback.xml中的模块名异常\n修改文件：{}\n异常信息：{}'
                        .format(filepath, err))

    def __alter_nacos_config(self, sqldir):
        """
        修改项目Nacos配置

        参数:
            sqldir (str): sql目录
        """
        files = os.listdir(sqldir)
        default_nacos_config_sql_prefix = self.templatedict[
            self.series + '.default_nacos_config_sql_prefix']
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        nacos_group_id_text = self.projectname.upper()
        default_group_id_text = default_project_name.upper()

        for filename in files:
            filepath = os.path.join(sqldir, filename)
            # 如果是目录继续递归
            if os.path.isdir(filepath):
                self.__alter_nacos_config(filepath)
            else:
                try:
                    # 如果是文件才进行修改
                    if filename.startswith(
                            default_nacos_config_sql_prefix):
                        with open(
                                filepath, 'r', encoding=self.DEFAULT_ENCODING
                        ) as srcfile, open(
                                '%s.bak' % filepath,
                                'w',
                                encoding=self.DEFAULT_ENCODING) as desfile:
                            self.messagehandle('正在修改：' + filename)
                            for line in srcfile:
                                if default_package_name in line:
                                    line = line.replace(
                                        default_package_name,
                                        self.packagename)
                                if default_project_name + '-' in line:
                                    line = line.replace(
                                        default_project_name + '-',
                                        self.projectname + '-')
                                if default_group_id_text in line:
                                    # 修改nacos配置中的group_id字段值
                                    line = line.replace(default_group_id_text, nacos_group_id_text)
                                if self.configdict['config.enable'] == 'True':
                                    line = self.__check_yml_or_sql_config(
                                        line, filename)
                                desfile.write(line)
                        # 移除旧文件
                        os.remove(filepath)
                        # 重命名备份文件为新文件
                        os.rename('%s.bak' % filepath, filepath)
                except Exception as err:
                    self.exceptionhandle(
                        '修改项目Nacos配置异常\n修改文件：{}\n异常信息：{}'.format(
                            filepath, err))

    def __check_yml_or_sql_config(self, line, filename):
        """
        检测yml配置文件

        参数:
            line (str): 行
            filename (str): 文件名
        """
        if 'localhost:3306/suoyi_uat' in line and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'mysql_ip_port_name')
        if 'username: root' in line and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'mysql_username')
        if 'password: password' in line and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'mysql_password')
        if 'host: localhost' in line and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'redis_host')
        if 'port: 6379' in line and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'redis_port')
        if ('password: \\r\\n' in line or 'password: \\n' in line) and filename.endswith('.sql'):
            line = self.__alert_yml_or_sql_config(line, 'redis_password')

        return line

    def __alert_yml_or_sql_config(self, line, type_):
        """
        修改yml配置文件

        参数:
            line (str): 行
            type_ (str): 修改类型
        """
        if type_ == 'mysql_ip_port_name':
            mysql_ip = self.configdict['database.ip']
            mysql_port = self.configdict['database.port']
            mysql_name = self.configdict['database.name']
            return line.replace('localhost:3306/suoyi_uat',
                                mysql_ip + ':' + mysql_port + '/' + mysql_name)
        if type_ == 'mysql_username':
            mysql_username = self.configdict['database.username']
            return line.replace('username: root',
                                'username: ' + mysql_username)
        if type_ == 'mysql_password':
            mysql_password = self.configdict['database.password']
            return line.replace('password: password',
                                'password: ' + mysql_password)
        if type_ == 'redis_host':
            redis_ip = self.configdict['redis.ip']
            return line.replace('host: localhost', 'host: ' + redis_ip)
        if type_ == 'redis_port':
            redis_port = self.configdict['redis.port']
            return line.replace('port: 6379', 'port: ' + redis_port)
        if type_ == 'redis_password':
            redis_password = self.configdict['redis.password']
            return line.replace('password: \\r\\n',
                                'password: ' + redis_password + '\\r\\n').replace('password: \\n',
                                                                                  'password: ' + redis_password + '\\n')
        return line

    def __alter_pom_xml(self, rootpath):
        """
        修改项目pom.xml文件

        参数：
            rootpath (str): 根目录
        """
        default_artifactid_prefix = self.templatedict[self.series +
                                                      '.default_artifactid_prefix']
        default_group_id = self.templatedict[self.series + '.default_group_id']
        default_site_name = self.templatedict[self.series +
                                              '.default_site_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        files = os.listdir(rootpath)
        for filename in files:
            filepath = os.path.join(rootpath, filename)
            # 如果是目录继续递归
            if os.path.isdir(filepath):
                self.__alter_pom_xml(filepath)
            else:
                try:
                    # 如果是文件才进行修改
                    if filename.endswith('pom.xml'):
                        with open(
                                filepath, 'r', encoding=self.DEFAULT_ENCODING
                        ) as srcfile, open(
                                '%s.bak' % filepath,
                                'w',
                                encoding=self.DEFAULT_ENCODING) as desfile:
                            self.messagehandle('正在修改：' + filename)
                            for line in srcfile:
                                if default_group_id in line and '<groupId>' in line:
                                    line = line.replace(
                                        default_group_id,
                                        self.groupid)
                                if default_artifactid_prefix in line and '<artifactId>' in line:
                                    line = line.replace(
                                        default_artifactid_prefix,
                                        self.artifactid)
                                if '<name>' in line or '<module>' in line:
                                    line = line.replace(
                                        default_project_name,
                                        self.projectname)
                                if 'version>' in line:
                                    line = line.replace(
                                        default_artifactid_prefix,
                                        self.artifactid)
                                if default_site_name in line:
                                    line = line.replace(
                                        default_site_name,
                                        self.sitename)
                                line = line.replace(
                                    default_artifactid_prefix,
                                    self.projectname)
                                desfile.write(line)
                        # 移除旧文件
                        os.remove(filepath)
                        # 重命名备份文件为新文件
                        os.rename('%s.bak' % filepath, filepath)
                except Exception as err:
                    self.exceptionhandle(
                        '修改项目pom.xml文件异常\n修改文件：{}\n异常信息：{}'.format(
                            filepath, err))

    def __alter_project_dir(self):
        """修改目录名"""
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_project_name = self.templatedict[self.series +
                                                 '.default_project_name']
        default_module_name_tuple = tuple(
            eval(self.templatedict[self.series + '.default_module_name_tuple']))
        for module_name in default_module_name_tuple:
            replace_module_name = module_name.replace('#', os.path.sep)
            if not os.path.exists(
                    os.path.join(self.rootpath, replace_module_name)):
                continue
            src_main_java_dir = os.path.join(self.rootpath,
                                             replace_module_name,
                                             'src/main/java')
            if os.path.exists(src_main_java_dir):
                source_dir = os.path.join(
                    src_main_java_dir,
                    self.packagename.replace('.', os.path.sep))
                old_dir = os.path.join(
                    src_main_java_dir, default_package_name.replace('.', os.path.sep))
                if not os.path.exists(source_dir):
                    os.makedirs(source_dir)
                # 移动目录及文件
                self.move_dir(old_dir,
                              source_dir)
                # 删除空目录
                self.__rm_empty_sub_dir(src_main_java_dir)
                if module_name.find('#') == -1:
                    os.rename(
                        os.path.join(self.rootpath, module_name),
                        os.path.join(
                            self.rootpath, module_name.replace(
                                default_project_name, self.projectname)
                        ))
                else:
                    tarpath = os.path.join(
                        self.rootpath,
                        module_name.split('#')[0],
                        module_name.split('#')[1].replace(
                            default_project_name, self.projectname)
                    )
                    os.rename(os.path.join(self.rootpath, replace_module_name),
                              tarpath)
            else:
                os.rename(
                    os.path.join(self.rootpath, module_name),
                    os.path.join(
                        self.rootpath, module_name.replace(
                            default_project_name, self.projectname)
                    ))
            self.messagehandle('正在修改：' + replace_module_name)
        if len(self.rootname) > 0:
            os.rename(self.rootpath,
                      os.path.join(self.targetdir, self.projectdirname))
            self.messagehandle('正在修改：' + self.rootname)

    def __rm_empty_sub_dir(self, dir_path):
        """移除所有空的子目录"""
        # 如果不是文件夹 直接返回
        if not os.path.isdir(dir_path):
            return
        # 如果文件夹下没有内容 则移除
        if not os.listdir(dir_path):
            self.messagehandle('移除文件夹' + dir_path)
            os.rmdir(dir_path)
            return
        # 如果文件夹下有内容 遍历并进行递归
        for sub_dir in os.listdir(dir_path):
            dir_join_path = os.path.join(dir_path, sub_dir)
            self.__rm_empty_sub_dir(dir_join_path)

    def __alter_application_class(self):
        """修改Application启动类中的站点名称"""
        default_package_name = self.templatedict[self.series +
                                                 '.default_package_name']
        default_module_name_tuple = tuple(
            eval(self.templatedict[self.series + '.default_module_name_tuple']))
        default_package_dir_path = default_package_name.replace('.', os.path.sep)

        for module_name in default_module_name_tuple:
            replace_module_name = module_name.replace('#', os.path.sep)
            if not os.path.exists(
                    os.path.join(self.rootpath, replace_module_name)):
                continue
            src_main_java_dir = os.path.join(self.rootpath,
                                             replace_module_name,
                                             'src/main/java')
            if os.path.exists(src_main_java_dir):
                # 从这个src/main/java/com/sfy/suoyi下开始递归
                application_parent_dir_path = os.path.join(
                    src_main_java_dir, default_package_dir_path)
                self.__replace_application_java_site_name(application_parent_dir_path)

    def __replace_application_java_site_name(self, path):
        """递归找到Application.java类修改其中的启动文本"""
        # 不存在 则退出
        if not os.path.exists(path):
            return
        # 如果是文件夹且有内容则进行递归
        if os.path.isdir(path) and os.listdir(path):
            for sub_dir in os.listdir(path):
                dir_join_path = os.path.join(path, sub_dir)
                self.__replace_application_java_site_name(dir_join_path)
            return
        # 如果当前目录是文件 且是Application.java结尾
        if os.path.isfile(path) and path.endswith('Application.java'):
            self.messagehandle('正在修改：' + path)
            default_site_name = self.templatedict[self.series +
                                                  '.default_site_name']
            self.__open_file_and_replace_keyword(path, default_site_name, self.sitename)
            # 修改Application.java启动类的SuoYi开头几个字母
            self.__open_application_and_replace_keyword(path)

    def __open_file_and_replace_keyword(self, file_path, find_key, replace_text):
        """打开文件并替换关键字"""
        with open(file_path, 'r',
                  encoding=self.DEFAULT_ENCODING) as srcfile, \
                open(
                    '%s.bak' % file_path,
                    'w',
                    encoding=self.DEFAULT_ENCODING) as desfile:
            for line in srcfile:
                if find_key in line:
                    line = line.replace(
                        find_key, replace_text)
                desfile.write(line)
        # 移除旧文件
        os.remove(file_path)
        # 重命名备份文件为新文件
        os.rename('%s.bak' % file_path, file_path)

    def __open_application_and_replace_keyword(self, file_path):
        """打开Application.java文件并替换类名及文件名"""
        default_project_name = self.templatedict[self.series + '.default_project_name']
        hump_default_project_name = self.applicationprefix[0].upper() + self.applicationprefix[1:]
        is_replace_class_name = False
        upper_char_find_key = default_project_name[0].upper() + default_project_name[1:]
        with open(file_path, 'r',
                  encoding=self.DEFAULT_ENCODING) as srcfile, \
                open(
                    '%s.bak' % file_path,
                    'w',
                    encoding=self.DEFAULT_ENCODING) as desfile:
            for line in srcfile:
                if upper_char_find_key in line:
                    line = line.replace(
                        upper_char_find_key, hump_default_project_name)
                    is_replace_class_name = True
                desfile.write(line)
        # 移除旧文件
        os.remove(file_path)
        if is_replace_class_name:
            # 重命名为新类名
            src_file_name_index = file_path.rfind(os.path.sep) + 1
            new_class_file_name = file_path[src_file_name_index:].replace(upper_char_find_key, hump_default_project_name)
            os.rename('%s.bak' % file_path, file_path[0:src_file_name_index] + new_class_file_name)
        else:
            # 重命名备份文件为新文件
            os.rename('%s.bak' % file_path, file_path)
